import Bench1.Coroutines.go
import Bench1.Coroutines.loop
import Bench1.Coroutines.pause
import Bench1.Coroutines.resume
import kotlin.coroutines.Continuation
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
import kotlin.coroutines.intrinsics.createCoroutineUnintercepted
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
import kotlin.coroutines.resume
import kotlin.system.exitProcess

// java -cp "C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.2\plugins\Kotlin\kotlinc\lib\kotlin-stdlib.jar;." Bench1Kt
object Bench1 {
    object Coroutines {
        private val readyStack: ArrayList<Continuation<Unit>> = ArrayList(10000)

        fun loop() {
            while (true)
                (readyStack.removeLastOrNull() ?: break).resume(Unit)
        }

        fun resume(c: Continuation<Unit>) {
            readyStack.add(c)
        }

        fun go(f: suspend () -> Unit) {
            f.createCoroutineUnintercepted(Continuation(EmptyCoroutineContext) {}).resume(Unit)
        }

        suspend fun pause(f: (Continuation<Unit>) -> Unit) {
            suspendCoroutineUninterceptedOrReturn {
                f.invoke(it)
                COROUTINE_SUSPENDED
            }
        }
    }

    class IntChan {
        private val waitQueue: ArrayDeque<Continuation<Unit>> = ArrayDeque()
        private var value: Int = 0
        private var hasValue: Boolean = false

        suspend fun put(v: Int) {
            if (hasValue)
                pause(waitQueue::add)
            value = v
            hasValue = true
            val c = waitQueue.removeFirstOrNull()
            if (c != null)
                resume(c)
        }

        suspend fun get(): Int {
            if (!hasValue)
                pause(waitQueue::add)
            hasValue = false
            val c = waitQueue.removeFirstOrNull()
            if (c != null)
                resume(c)
            return value
        }
    }

    private suspend fun generate(ch: IntChan) {
        var i = 2
        while (true)
            ch.put(i++)
    }

    private suspend fun filter(input: IntChan, output: IntChan, prime: Int) {
        while (true) {
            val v = input.get()
            if (v % prime != 0)
                output.put(v)
        }
    }

    fun main() {
        val ch = IntChan()
        go { generate(ch) }
        go {
            var ch0 = ch
            var i = 0
            while (true) {
                val prime = ch0.get()
                if (++i == 10000) {
                    println(prime)
                    exitProcess(0)
                }
                val ch1 = ch0
                val ch2 = IntChan()
                go { filter(ch1, ch2, prime) }
                ch0 = ch2
            }
        }
        loop()
    }
}

fun main() {
    Bench1.main()
}
